﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Knlght.Domain
{
    public static class IQueryableExtensions
    {

        /// <summary>
        /// 动态排序
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="propertyName">属性名</param>
        /// <param name="ascending">是否升序</param>
        /// <returns></returns>
        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool ascending)
            where T : class
        {
            if (string.IsNullOrEmpty(propertyName))
            {
                return source;
            }
            var type = typeof(T);
            var property = type.GetProperty(propertyName);
            if (property == null)
                throw new ArgumentException("propertyName", "不存在");

            var param = Expression.Parameter(type, "p");
            Expression propertyAccessExpression = Expression.MakeMemberAccess(param, property);
            var orderByExpression = Expression.Lambda(propertyAccessExpression, param);

            var methodName = ascending ? "OrderBy" : "OrderByDescending";
            var resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { type, property.PropertyType },
                source.Expression, Expression.Quote(orderByExpression)); //第三个类型为泛型的类型
            return source.Provider.CreateQuery<T>(resultExp);
        }

        public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, QueryRequest<T> request)
              where T : class
        {
            //创建表达式变量参数
            var parameter = Expression.Parameter(typeof(T), "o");
            for (var i = 0; i < request.OrderBys.Count; i++)
            {
                var item = request.OrderBys.ElementAt(i);
                //根据属性名获取属性
                var property = typeof(T).GetProperty(item.Key);
                //创建一个访问属性的表达式
                var propertyAccess = Expression.MakeMemberAccess(parameter, property);
                var orderByExp = Expression.Lambda(propertyAccess, parameter);
                string orderName;
                 if (i > 0) { orderName = item.Value ? "ThenByDescending" : "ThenBy"; } else { orderName = item.Value ? "OrderByDescending" : "OrderBy"; }
                 
                var resultExp = Expression.Call(typeof(Queryable),
                    orderName, new Type[]
                    {
                        typeof (T),
                        property.PropertyType
                    },
                source.Expression, Expression.Quote(orderByExp));
                source.Provider.CreateQuery<T>(resultExp);
            }
            return source;
        }
    }
}
